package com.ljf.daily.costmanagement.domain.validator;

import com.ljf.common.domain.validator.BasisValidator;
import com.ljf.common.exception.ServiceException;
import com.ljf.common.utils.EnumUtil;
import com.ljf.common.utils.StringUtil;
import com.ljf.config.domain.repository.ConfigDatRecordSymbolRepository;
import com.ljf.config.dto.query.ConfigDatRecordSymbolQuery;
import com.ljf.config.enums.ConfigDatRecordSymbolTypeEnum;
import com.ljf.daily.costmanagement.domain.repository.DailyDatCostManagementDetailRepository;
import com.ljf.daily.costmanagement.domain.repository.DailyDatCostManagementRepository;
import com.ljf.daily.makemoneysoftware.domain.repository.DailyDatMakeMoneySoftwareRepository;
import com.ljf.daily.costmanagement.dto.command.DailyDatCostManagementCommand;
import com.ljf.daily.costmanagement.dto.command.DailyDatCostManagementConfigCommand;
import com.ljf.daily.costmanagement.dto.command.DailyDatCostManagementDetailCommand;
import com.ljf.daily.costmanagement.dto.query.DailyDatCostManagementDetailQuery;
import com.ljf.daily.costmanagement.dto.query.DailyDatCostManagementQuery;
import com.ljf.daily.makemoneysoftware.dto.query.DailyDatMakeMoneySoftwareQuery;
import com.ljf.daily.costmanagement.enums.DailyDatCostManagementActivitySignEnum;
import com.ljf.daily.costmanagement.enums.DailyDatCostManagementConfigDailyConsumptionEnum;
import com.ljf.daily.costmanagement.enums.DailyDatCostManagementConfigDifferentLevellingEnum;
import com.ljf.daily.costmanagement.model.DailyDatCostManagement;
import com.ljf.daily.costmanagement.vo.DailyDatCostManagementDetailVO;
import com.ljf.daily.costmanagement.vo.DailyDatCostManagementVO;
import com.ljf.daily.makemoneysoftware.vo.DailyDatMakeMoneySoftwareVO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 费用管理  业务校验
 *
 * @author LJF
 * @since 2025-03-30
 */
@Component
@AllArgsConstructor
public class DailyDatCostManagementValidator extends BasisValidator<DailyDatCostManagementQuery, DailyDatCostManagementCommand, DailyDatCostManagementVO, DailyDatCostManagement, DailyDatCostManagementRepository> {

    private final ConfigDatRecordSymbolRepository configDatRecordSymbolRepository;
    private final DailyDatMakeMoneySoftwareRepository makeMoneySoftwareRepository;
    private final DailyDatCostManagementDetailRepository detailRepository;

    @Override
    public void checkAdd(DailyDatCostManagementCommand command) throws ServiceException {
        super.checkAdd(command);
        int count = configDatRecordSymbolRepository.count(
                ConfigDatRecordSymbolQuery.builder()
                        .type(ConfigDatRecordSymbolTypeEnum.DAILY_DAT_COST_MANAGEMENT.getCode())
                        .code(command.getSymbolCode())
                        .build()
        );
        if (count <= 0) {
            throw new ServiceException("数据完整性校验异常: 标签编码不存在");
        }
    }

    /**
     * 更新待摊费用配置 业务校验
     *
     * @param command
     * @throws ServiceException
     */
    public void checkEditConfig(DailyDatCostManagementConfigCommand command) throws ServiceException {
        if (repository.count(DailyDatCostManagementQuery.builder().code(command.getCode()).build()) == 0) {
            throw new ServiceException("参数异常: 待摊费用不存在, 请刷新后重试");
        }
        if (StringUtil.isNotEmpty(command.getDifferentSoftwareCode()) || StringUtil.isNotEmpty(command.getWithholdSoftwareCode()) || StringUtil.isNotEmpty(command.getRestoreSoftwareCode())) {
            if (StringUtil.isEmpty(command.getAccountCode())) {
                throw new ServiceException("参数异常: 账户不能为空");
            }
            Set<String> softwareCodeSet = getSoftwareCode(command);
            List<DailyDatMakeMoneySoftwareVO> moneySoftwareList = makeMoneySoftwareRepository.selectList(
                    DailyDatMakeMoneySoftwareQuery.builder().codeList(new ArrayList<>(softwareCodeSet)).build()
            );
            if (softwareCodeSet.size() != moneySoftwareList.size()) {
                throw new ServiceException("参数异常: 软件不存在");
            }
            if (moneySoftwareList.stream().anyMatch(one -> !StringUtil.equals(one.getAccountCode(), command.getAccountCode()))) {
                throw new ServiceException("参数异常: 存在软件不属于账户");
            }
        }
        if (StringUtil.isNotEmpty(command.getDeductionSoftwareCode())) {
            int count = makeMoneySoftwareRepository.count(
                    DailyDatMakeMoneySoftwareQuery.builder().code(command.getDeductionSoftwareCode()).build()
            );
            if (count == 0) {
                throw new ServiceException("参数异常: 扣款软件不存在");
            }
        }
        if (command.getDifferentLevelling() != null) {
            if (!EnumUtil.checkCode(command.getDifferentLevelling(), DailyDatCostManagementConfigDifferentLevellingEnum.values())) {
                throw new ServiceException("参数异常: 差异调平编码不存在");
            }
            if (Objects.equals(command.getDifferentLevelling(), DailyDatCostManagementConfigDifferentLevellingEnum.YES.getCode())) {
                if (StringUtil.isEmpty(command.getLevellingSoftwareCode())) {
                    throw new ServiceException("参数异常: 调平软件不能为空");
                }
                int count = makeMoneySoftwareRepository.count(
                        DailyDatMakeMoneySoftwareQuery.builder().code(command.getLevellingSoftwareCode()).build()
                );
                if (count == 0) {
                    throw new ServiceException("参数异常: 调平软件不存在");
                }
            }
        }
        if (command.getDailyConsumption() != null && !EnumUtil.checkCode(command.getDailyConsumption(), DailyDatCostManagementConfigDailyConsumptionEnum.values())) {
            throw new ServiceException("参数异常: 日常消耗编码不存在");
        }
        if (StringUtil.isNotEmpty(command.getConsumeSoftwareCode())) {
            int count = makeMoneySoftwareRepository.count(
                    DailyDatMakeMoneySoftwareQuery.builder().code(command.getConsumeSoftwareCode()).build()
            );
            if (count == 0) {
                throw new ServiceException("参数异常: 消耗软件(外)不存在");
            }
        }
    }

    /**
     * 根据ID查询待摊费用配置 业务校验
     *
     * @param id
     * @throws ServiceException
     */
    public void checkQueryConfigById(Long id) throws ServiceException {
        if (id == null) {
            throw new ServiceException("参数异常: ID不能为空");
        }
        if (repository.countById(id) <= 0) {
            throw new ServiceException("参数异常: 数据不存在, 请刷新后重试");
        }
    }

    /**
     * 新增费用明细 业务校验
     *
     * @param command
     * @throws ServiceException
     */
    public void checkAddDetail(DailyDatCostManagementDetailCommand command) throws ServiceException {
        DailyDatCostManagementVO management = repository.selectOne(
                DailyDatCostManagementQuery.builder().code(command.getCode()).build(),
                "费用管理异常: 费用类型不存在",
                "费用管理异常: 数据异常"
        );
        if (Objects.equals(management.getActivitySign(), DailyDatCostManagementActivitySignEnum.NOT_ACTIVITY.getCode())) {
            throw new ServiceException("费用管理异常: 费用类型不活动");
        }
        if (StringUtil.isNotEmpty(command.getRemark()) && command.getRemark().length() > 17) {
            throw new ServiceException("参数异常: 备注不能大于17个字符");
        }
    }

    /**
     * 删除费用明细 业务校验
     *
     * @param id
     * @throws ServiceException
     */
    public void checkDeleteDetail(Long id) throws ServiceException {
        if (id == null) {
            throw new ServiceException("参数异常: ID不能为空");
        }
    }

    /**
     * 更新费用明细 业务校验
     *
     * @param command
     * @throws ServiceException
     */
    public void checkEditDetail(DailyDatCostManagementDetailCommand command) throws ServiceException {
        DailyDatCostManagementDetailVO model = detailRepository.selectById(command.getId());
        if (model == null) {
            throw new ServiceException("参数异常: 数据不存在, 请刷新后重试");
        }
        if (model.getQuantity().compareTo(command.getRemainQuantity()) < 0) {
            throw new ServiceException("业务异常: 剩余数量不能大于总数量");
        }
        if (StringUtil.isNotEmpty(command.getRemark()) && command.getRemark().length() > 17) {
            throw new ServiceException("参数异常: 备注不能大于17个字符");
        }
    }

    /**
     * 分页查询费用明细 业务校验
     *
     * @param query
     * @throws ServiceException
     */
    public void checkQueryDetail(DailyDatCostManagementDetailQuery query) throws ServiceException {
        if (query == null) {
            throw new ServiceException("参数异常: 空指针");
        }
    }

    /**
     * 根据ID查询费用明细 业务校验
     *
     * @param id
     * @throws ServiceException
     */
    public void checkQueryDetailById(Long id) throws ServiceException {
        if (id == null) {
            throw new ServiceException("参数异常: ID不能为空");
        }
        if (detailRepository.countById(id) <= 0) {
            throw new ServiceException("参数异常: 数据不存在, 请刷新后重试");
        }
    }

    /**
     * 消耗费用明细 业务校验
     *
     * @param command
     * @throws ServiceException
     */
    public void checkConsumeDetail(DailyDatCostManagementDetailCommand command) throws ServiceException {
        DailyDatCostManagementDetailVO model = detailRepository.selectById(command.getId());
        if (model == null) {
            throw new ServiceException("参数异常: 数据不存在, 请刷新后重试");
        }
        if (model.getRemainQuantity().compareTo(command.getRemainQuantity()) < 0) {
            throw new ServiceException("业务异常: 消耗数量不能大于剩余数量");
        }
        if (StringUtil.isNotEmpty(command.getRemark()) && command.getRemark().length() > 17) {
            throw new ServiceException("参数异常: 备注不能大于17个字符");
        }
    }

    /**
     * 获取软件编码
     *
     * @param command
     * @return
     */
    private static Set<String> getSoftwareCode(DailyDatCostManagementConfigCommand command) {
        Set<String> softwareCodeSet = new HashSet<>();
        if (StringUtil.isNotEmpty(command.getDifferentSoftwareCode())) {
            softwareCodeSet.add(command.getDifferentSoftwareCode());
        }
        if (StringUtil.isNotEmpty(command.getWithholdSoftwareCode())) {
            softwareCodeSet.add(command.getWithholdSoftwareCode());
        }
        if (StringUtil.isNotEmpty(command.getRestoreSoftwareCode())) {
            softwareCodeSet.add(command.getRestoreSoftwareCode());
        }
        return softwareCodeSet;
    }

}
